﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.IO;
using System.Linq;
using NLog;

namespace TfsGiggle.Core.Parsers
{
    /// <summary>
    /// Provides IParsers based on the MEF registerered file-handler-parsers.
    /// Implement IParserFactory/IParser and place in the Extensions folder to get it loaded.
    /// </summary>
    public class ParserFactoryManager : IDisposable
    {
        static LoggerWithEventId _logger = (LoggerWithEventId)LogManager.GetCurrentClassLogger(typeof(LoggerWithEventId));
        static readonly Lazy<ParserFactoryManager> _instance = new Lazy<ParserFactoryManager>(() => new ParserFactoryManager());
        CompositionContainer _container;

        /// <summary>
        /// This instance
        /// </summary>
        public static ParserFactoryManager Instance
        {
            get
            {
                return _instance.Value;
            }
        }

        /// <summary>
        /// MEF imported parserfactories
        /// </summary>
        [ImportMany]
        public IEnumerable<IParserFactory> ParserFactories = null;

        ParserFactoryManager()
        {
            const string ExtensionsFolderName = "Extensions";
            try
            {
                using (var catalog = new AggregateCatalog())
                {
                    var extensionsFolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ExtensionsFolderName);
                    catalog.Catalogs.Add(new DirectoryCatalog("."));
                    if (Directory.Exists(extensionsFolder))
                    {
                        catalog.Catalogs.Add(new DirectoryCatalog(ExtensionsFolderName));
                    }
                    _container = new CompositionContainer(catalog);
                    _container.ComposeParts(this);
                }
            }
            catch (Exception ex)
            {
                _logger.ErrorExceptionWithEventId(EventId.ParserFactory, "ParserFactory ctor", ex);
            }
        }

        /// <summary>
        /// Get a parser that can handle this fileextension
        /// </summary>
        /// <param name="fileExtension"></param>
        /// <returns></returns>
        public static IParser GetParser(string fileExtension)
        {
            if (ParserFactoryManager.Instance.ParserFactories == null) return null;
            if (string.IsNullOrWhiteSpace(fileExtension))
            {
                _logger.Warn("No fileExtension");
                return null;
            }

            // Give each factory a chance to provide a parser; give your parser a priority higher than 0 
            // to override the two builtin parsers; Roslyn (C#) and CTags for everything else.
            foreach (var factory in ParserFactoryManager.Instance.ParserFactories.OrderByDescending( p => p.Priority ))
            {
                var parser = factory.GetParser(fileExtension);
                if (parser != null)
                {
                    return parser;
                }
            }
            _logger.Trace("No parser found for fileExtension:{0}", fileExtension);
            return null;
        }

        void Dispose(bool isDisposing)
        {
            if (isDisposing)
            {
                if (_container != null)
                {
                    _container.Dispose();
                    _container = null;
                }
            }
        }

        /// <summary>
        /// Go away
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
    }
}